// Copyright (c) 2020 InCore Semiconductors Pvt. Ltd. see LICENSE.incore for more details on licensing terms
/*
Author: Neel Gala, neelgala@incoresemi.com
Created on: Friday 01 May 2020 05:19:02 PM IST

*/
package Soc ;

import FIFOF          :: * ;
import Vector         :: * ;
import SpecialFIFOs   :: * ;
import FIFOF          :: * ;
import Connectable    :: * ;
import GetPut         :: * ;
import Clocks         :: * ;

`include "Logger.bsv"
`include "Soc_map.bsv"
import Soc_instances  :: * ;
import qspi           :: * ;
import uart           :: * ;
import bootconfig     :: * ;
import clint          :: * ;
import plic           :: * ;
import gpio           :: * ;
import pwm            :: * ;
import spi            :: * ;
import spi_cluster    :: * ;
import ram2rw         :: * ;
import rom            :: * ;
import ram1rw         :: * ;
import riscv_debug    :: * ;
import debug_loop     :: * ;
import axi4           :: * ;
import axi4l          :: * ;
import apb            :: * ;
import ccore          :: * ;
import ccore_types    :: * ;
import bridges        :: * ;
import DCBus          :: * ;
import gateway        :: * ;
import riscv_debug_types :: * ;

interface Ifc_Soc;
    (*prefix="uart0"*)
    interface RS232#(16) ifc_uart0;
    (*always_ready, always_enabled, prefix=""*)
    method Action boot_config ((*port = "boot_config"*) Bit#(2) conf);
    (*prefix=""*)
    interface GPIO#(22)  ifc_gpio;
    (*prefix=""*)
    interface PWMIO#(`channels, `comp_out_en)  ifc_pwm;
    (*prefix="spi0"*)
    interface SPI_IO#(1) ifc_spi0; // used for flash
    (*prefix="spi1"*)
    interface SPI_IO#(2) ifc_spi1;
    (*prefix="spi2"*)
    interface SPI_IO#(1) ifc_spi2;
    (*prefix="ddr"*)
    interface Ifc_axi4_master#(`AXI4ID, `paddr, 64, `User) ddr_slave;
    (*prefix="open"*)
    interface Ifc_axi4_master#(`AXI4ID, `paddr, 64, `User) open_slave;
    interface Ifc_axi4_slave #(`AXI4ID, `paddr, 64, `User) debug_master;
    (*prefix="eth"*)
    interface Ifc_axi4l_master #(`paddr, 32, `User) eth_slave;
    (*always_enabled,always_ready, prefix="eth"*)
    method Action ma_eth_inp(Bit#(1) int_in);
    interface Ifc_hart_to_debug debug_server;
`ifdef rtldump
    interface Get#(DumpType) io_dump;
`endif
  (*always_ready, always_enabled, prefix="qspi0"*)
  interface QSPI_out qspi_io;
endinterface:Ifc_Soc

(*synthesize*)
module mkSoc(Ifc_Soc);

  let debug_loop  <- mkdebug_loop;
  let bootrom     <- mkbootrom;
  let uart0       <- mkuart0;
  let bootconfig  <- mkbootconfig;
  let gpio        <- mkgpio;
  let clint       <- mkclint;
  let plic        <- mkplic;
  let ocm         <- mkocm;
  let pwm         <- mkpwm;
  let ccore       <- mkccore_axi4(`resetpc, 0);
  let qspi        <- mkqspi();

  let axi4_xbar   <- mkaxi4_crossbar;
  let axi4l_xbar  <- mkaxi4l_crossbar;
  let apb_fabric  <- mkapb_interconnect;

  let axi2apb     <- mkaxi2apb_bridge ;
  let axi2axil    <- mkaxi2axil_bridge ;

  Ifc_axi4_slave   #(`AXI4ID,`paddr, 64, `User) axi4_err    <- mkaxi4_err_2;
  Ifc_apb_slave    #(`paddr, 32, `User)         apb_err     <- mkapb_err;
  Ifc_axi4l_slave  #(`paddr, 32, `User)         axi4l_err   <- mkaxi4l_err_2;
  Ifc_spi_cluster spi_cluster <- mkspi_cluster;

  let clk <- exposeCurrentClock;
  let rst <- exposeCurrentReset;
  Wire#(Bit#(1)) eth_intc_irpt <- mkBypassWire();
  Ifc_gateway2 m_gateways [`plic_interrupt_src];

  let gpio_int = 0;
  let uart_int = gpio_int + `gpio_interrupt_size;
  let pwm_int  = uart_int + `uart_interrupt_size;
  let spi_int  = pwm_int  + `pwm_interrupt_size ;
  let qspi_int = spi_int  + `spi_interrupt_size ;
  let eth_int  = qspi_int + `qspi_interrupt_size ;
  let stale_int =  eth_int  + `eth_interrupt_size;

  for(Integer idx = 0; idx < `gpio_interrupt_size ; idx = idx + 1) begin
      let gpio_idx = idx + gpio_int ;
    m_gateways[gpio_idx] <- mk_gateway2(1, clk, rst, 0, 1);
    mkConnection(m_gateways[gpio_idx].ma_input, gpio.device.sb_gpio_to_plic[idx]);
  end

  for(Integer idx = 0; idx < `uart_interrupt_size ; idx = idx + 1) begin
      let uart_idx = idx + uart_int ;
    m_gateways[uart_idx] <- mk_gateway2(1, clk, rst, 0, 1);
    mkConnection(m_gateways[uart_idx].ma_input, uart0.device.interrupt);
  end

  for(Integer idx = 0; idx < `pwm_interrupt_size ; idx = idx + 1) begin
      let pwm_idx = idx + pwm_int;
    m_gateways[pwm_idx] <- mk_gateway2(1, clk, rst, 0, 1);
    mkConnection(m_gateways[pwm_idx].ma_input, pwm.device.sb_interrupt[idx]);
  end

  for(Integer idx = 0; idx < `spi_interrupt_size ; idx = idx + 1) begin
      let spi_idx = idx + spi_int;
    m_gateways[spi_idx] <- mk_gateway2(1, clk, rst, 0, 1);
    mkConnection(m_gateways[spi_idx].ma_input, spi_cluster.spi_sb_interrupt[idx]);
  end

  for(Integer idx = 0; idx < `qspi_interrupt_size ; idx = idx + 1) begin
      let qspi_idx = idx + qspi_int;
    m_gateways[qspi_idx] <- mk_gateway2(1, clk, rst, 0, 1);
    mkConnection(m_gateways[qspi_idx].ma_input, qspi.device.mv_interrupts[idx]);
  end

  for(Integer idx = 0; idx < `eth_interrupt_size ; idx = idx + 1) begin
        let eth_idx = idx + eth_int;
      m_gateways[eth_idx] <- mk_gateway2(1, clk, rst, 1, 1);
      mkConnection(m_gateways[eth_idx].ma_input, eth_intc_irpt);
  end

  for(Integer idx = 0; idx < `unused_interrupt_size ; idx = idx + 1) begin
        let stale_idx = idx + stale_int;
      m_gateways[stale_idx] <- mk_gateway2(1, clk, rst, 0, 1);
      mkConnection(m_gateways[stale_idx].ma_input, 1'b0);
  end

  for (Integer i = 0; i<`plic_interrupt_src; i = i + 1) begin
    /*doc:rule: */
    rule rl_clear_pending_interrupt(plic.device.sb_to_gateway[i] == 1);
      m_gateways[i].ma_complete();
    endrule:rl_clear_pending_interrupt
  end
  Bit#(`plic_interrupt_src) _irqs;
  for(Integer idx = 0; idx < `plic_interrupt_src; idx = idx + 1) begin
      _irqs[idx] = m_gateways[idx].mv_interrupt;
  end

 	mkConnection(ccore.sb_clint_mtime, clint.device.sb_clint_mtime);
 	mkConnection(ccore.sb_clint_mtip,  clint.device.sb_clint_mtip);
 	mkConnection(ccore.sb_clint_msip,  clint.device.sb_clint_msip);

 	mkConnection(ccore.sb_plic_meip, pack(plic.device.sb_to_targets[0]));
  mkConnection(plic.device.sb_frm_gateway, _irqs);

 	mkConnection(ccore.master_d, axi4_xbar.v_from_masters[`DMem_master_num]);
 	mkConnection(ccore.master_i, axi4_xbar.v_from_masters[`IMem_master_num]);

 	mkConnection(axi4_xbar.v_to_slaves[`APBBridge_slave_num], axi2apb.axi4_side);
 	mkConnection(axi2apb.apb_side, apb_fabric.frm_master);
 	mkConnection(axi4_xbar.v_to_slaves[`AXI4LBridge_slave_num], axi2axil.axi4_side);
 	mkConnection(axi2axil.axi4l_side, axi4l_xbar.v_from_masters[0]);
 	mkConnection(axi4_xbar.v_to_slaves[`AXI4Err_slave_num], axi4_err);
 	mkConnection(axi4_xbar.v_to_slaves[`QSPI_mem_slave_num], qspi.device.mem_slave);

  mkConnection(apb_fabric.v_to_slaves[`Debug_slave_num], debug_loop.slave);
  mkConnection(apb_fabric.v_to_slaves[`Rom_slave_num]  , bootrom.slave);
  mkConnection(apb_fabric.v_to_slaves[`Uart0_slave_num], uart0.slave);
  mkConnection(apb_fabric.v_to_slaves[`Pwm_slave_num],   pwm.slave);
  mkConnection(apb_fabric.v_to_slaves[`SPICluster_slave_num], spi_cluster.slave);
  mkConnection(apb_fabric.v_to_slaves[`Bootconfig_slave_num], bootconfig.slave);
  mkConnection(apb_fabric.v_to_slaves[`Gpio_slave_num],  gpio.slave);
  mkConnection(apb_fabric.v_to_slaves[`ApbErr_slave_num], apb_err);
 	mkConnection(apb_fabric.v_to_slaves[`QSPI_cfg_slave_num], qspi.slave);

  mkConnection(axi4l_xbar.v_to_slaves[`Clint_slave_num], clint.slave);
  mkConnection(axi4l_xbar.v_to_slaves[`Plic_slave_num],  plic.slave);
  mkConnection(axi4l_xbar.v_to_slaves[`Ocm_slave_num ],  ocm.slave);
  mkConnection(axi4l_xbar.v_to_slaves[`AXI4LErr_slave_num], axi4l_err);


  interface ifc_uart0 = uart0.device.io;
  interface ifc_gpio  = gpio.device.io;
  interface ifc_pwm   = pwm.device.io;
  interface ifc_spi0  = spi_cluster.spi0_io;
  interface ifc_spi1  = spi_cluster.spi1_io;
  interface ifc_spi2  = spi_cluster.spi2_io;

  interface debug_master = axi4_xbar.v_from_masters[`Debug_master_num];
  interface debug_server = ccore.debug_server;

  interface ddr_slave    = axi4_xbar.v_to_slaves[`DDR_slave_num];
  interface open_slave   = axi4_xbar.v_to_slaves[`Open_slave_num];
  interface eth_slave   = axi4l_xbar.v_to_slaves[`Eth_slave_num];
  method boot_config = bootconfig.device.io;
  method Action ma_eth_inp(Bit#(1) int_in);
    eth_intc_irpt <= int_in;
  endmethod:ma_eth_inp

`ifdef rtldump
  interface io_dump= ccore.io_dump;
`endif
  interface qspi_io = qspi.device.io;
endmodule:mkSoc

endpackage: Soc

